<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>树形多选框</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <style>
        .tree-container {
            position: absolute;
            z-index: 1000;
            background: white;
            border: 1px solid #ddd;
            width: 100%;
            max-height: 300px;
            overflow-y: auto;
            display: none;
        }
        .tree-node {
            padding-left: 20px;
            cursor: pointer;
        }
        .tree-node:hover {
            background-color: #f8f9fa;
        }
        .tree-toggle {
            cursor: pointer;
            margin-right: 5px;
        }
        .input-container {
            position: relative;
        }
        .selected-nodes {
            margin-top: 10px;
        }
    </style>
</head>
{% verbatim %}
<body>
    <div id="app" class="container mt-4">
        <div class="card">
            <div class="card-header">
                <h5>树形多选框</h5>
            </div>
            <div class="card-body">
                <div class="input-container">
                    <input 
                        class="form-control" 
                        @click="toggleTree"
                        readonly
                        placeholder="请选择节点">
                    <div class="tree-container" ref="treeContainer">
                        <tree-node 
                            v-for="node in treeData" 
                            :key="node.id" 
                            :node="node">
                        </tree-node>
                    </div>
                    <div class="selected-nodes">
                        <span v-for="node in selectedNodes" :key="node.id" class="badge bg-primary me-1">
                            {{ node.label }}
                        </span>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        const { createApp, ref,watch } = Vue;

        const TreeNode = {
            props: ['node'],
            template: `
                <div class="tree-node">
                    <span class="tree-toggle" @click.stop="toggleExpand" v-if="node.children">
                        {{ node.expanded ? '−' : '+' }}
                    </span>
                    <input 
                        type="checkbox" 
                        :indeterminate.prop="node.indeterminate"
                        v-model="node.checked"
                        @click.stop="toggleCheck">
                    <span @click.stop="selectNode">{{ node.label }}</span>
                    <div v-if="node.expanded && node.children" class="children">
                        <tree-node 
                            v-for="child in node.children" 
                            :key="child.id" 
                            :node="child"
                            @update:checked="updateParentState">
                        </tree-node>
                    </div>
                </div>
            `,
            methods: {
                toggleExpand() {
                    this.node.expanded = !this.node.expanded;
                },
                toggleCheck() {
                    this.node.checked = !this.node.checked;
                    this.node.indeterminate = false;
                    this.updateChildrenState(this.node, this.node.checked);
                    this.$emit('update:checked', this.node.checked);
                },
                selectNode() {
                    this.toggleCheck();
                },
                updateChildrenState(node, checked) {
                    if (node.children) {
                        node.children.forEach(child => {
                            child.checked = checked;
                            child.indeterminate = false;
                            this.updateChildrenState(child, checked);
                        });
                    }
                },
                updateParentState() {
                    if (this.node.parent) {
                        const parent = this.node.parent;
                        const checkedCount = parent.children.filter(c => c.checked).length;
                        const indeterminateCount = parent.children.filter(c => c.indeterminate).length;
                        
                        if (checkedCount === 0 && indeterminateCount === 0) {
                            parent.checked = false;
                            parent.indeterminate = false;
                        } else if (checkedCount === parent.children.length) {
                            parent.checked = true;
                            parent.indeterminate = false;
                        } else {
                            parent.checked = false;
                            parent.indeterminate = true;
                        }
                        
                        // 递归更新所有祖先节点
                        if (parent.parent) {
                            this.$emit('update:checked', parent.checked);
                        }
                    }
                }
            }
        };

        const exampleData = [
            {
                id: 1,
                label: '根节点1',
                expanded: false,
                checked: false,
                indeterminate: false,
                children: [
                    {
                        id: 2,
                        label: '子节点1-1',
                        checked: false,
                        indeterminate: false,
                        parent: null,
                        children: [
                            { id: 3, label: '子节点1-1-1', checked: false, indeterminate: false, parent: null },
                            { id: 4, label: '子节点1-1-2', checked: false, indeterminate: false, parent: null }
                        ]
                    },
                    { id: 5, label: '子节点1-2', checked: false, indeterminate: false, parent: null }
                ]
            },
            {
                id: 6,
                label: '根节点2',
                expanded: false,
                checked: false,
                indeterminate: false,
                children: [
                    { id: 7, label: '子节点2-1', checked: false, indeterminate: false, parent: null },
                    { id: 8, label: '子节点2-2', checked: false, indeterminate: false, parent: null }
                ]
            }
        ];

        // 设置父节点引用
        const setParentRef = (nodes, parent = null) => {
            nodes.forEach(node => {
                node.parent = parent;
                if (node.children) {
                    setParentRef(node.children, node);
                }
            });
        };
        setParentRef(exampleData);

        const app = createApp({
            components: { TreeNode },
            setup() {
                const treeData = ref(exampleData);
                const treeContainer = ref(null);
                const selectedNodes = ref([]);

                const toggleTree = () => {
                    const container = treeContainer.value;
                    container.style.display = container.style.display === 'block' ? 'none' : 'block';
                };

                // 计算选中的节点
                const updateSelectedNodes = () => {
                    selectedNodes.value = [];
                    const traverse = (nodes) => {
                        nodes.forEach(node => {
                            if (node.checked) {
                                selectedNodes.value.push(node);
                            }
                            if (node.children) {
                                traverse(node.children);
                            }
                        });
                    };
                    traverse(treeData.value);
                };

                // 监听树数据变化
                watch(treeData, () => {
                    updateSelectedNodes();
                }, { deep: true });

                return { treeData, treeContainer, selectedNodes, toggleTree };
            }
        });

        app.component('tree-node', TreeNode);
        app.mount('#app');
    </script>
</body>
  {% endverbatim %} <!-- 添加此行 -->
</html>